/*
 * Copyright 2008-2009 Daniel Cachapa <cachapa@gmail.com>
 * 
 * This program is distributed under the terms of the GNU General Public Licence Version 3
 * The licence can be read in its entirety in the LICENSE.txt file accompaning this source code,
 * or at: http://www.gnu.org/copyleft/gpl.html
 * 
 * This file is part of WeightWatch.
 *
 * WeightWatch is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * WeightWatch is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the WeightWatch source code. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package net.cachapa.weightwatch;

import com.sun.lwuit.Font;
import net.cachapa.weightwatch.util.MyDate;
import com.sun.lwuit.Graphics;
import java.util.Vector;
import net.cachapa.weightwatch.util.L;

public class LineChartYear {

	private final static Font font = Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL);
	private final static int fontPadding = 2;
	private final static double minMaxPadding = 0.2;
	private final static int tickSize = 3;
	
	private int leftPadding,  rightPadding,  topPadding,  bottomPadding;
	private int labelWidth,  labelHeight;
	private int minValue;
	private double maxValue;
	private double verticalStep,  horizontalStep;
	private Preferences prefs;

	public LineChartYear() {
		labelWidth = font.stringWidth("88");
		labelHeight = font.getHeight();
		leftPadding = labelWidth + 2 * fontPadding;
		rightPadding = fontPadding + labelWidth / 2;
		topPadding = labelHeight / 2;
		bottomPadding = labelHeight + 2 * fontPadding;
	}

	public void drawChart(Graphics g, int width, int height, MyDate date, Database db, Preferences prefs) {
		this.prefs = prefs;
		int year = date.getYear();
		Vector values = db.getYear(year);
		determineMinMax(values);
		verticalStep = (height - bottomPadding - topPadding) / (double) (maxValue - minValue);
		horizontalStep = (width - rightPadding - leftPadding) / (double) MyDate.getDaysInYear(year);
		
		g.setColor(0xffffff);
		g.fillRect(0, 0, width, height);
		
		drawAxis(g, width, height, date, prefs);
		g.setClip(leftPadding+1, topPadding, width, height - topPadding - bottomPadding);
//		if (prefs.getPlan() != null) {
//			drawPlan(g, year, prefs.getPlan());
//		}
		Value prevValue = null, nextValue = null;
//		if (values.size() != 0) {
//			prevValue = db.getValueBefore(((Value)values.firstElement()).date);
//			nextValue = db.getValueAfter(((Value)values.lastElement()).date);
//		}
		drawLine(g, values, prevValue, nextValue);
	}

	private void drawAxis(Graphics g, int width, int height, MyDate date, Preferences prefs) {
		g.setFont(font);
		int bottom = height - bottomPadding;
		int months = 12;

		// These filters are here so that there is no overlapping between the axis labels
		int verticalFilter = 1;
		while (labelHeight > verticalStep * verticalFilter) {
			verticalFilter++;
		}
		
		double sum = 0;
		g.setColor(0x00000000);
		// Labels and ticks for the Y scale
		for (int i = 0; i <= (maxValue - minValue); i++) {
			sum = bottom - verticalStep * i;
			if (i % verticalFilter == 0) {
				g.drawString(String.valueOf(minValue + i), fontPadding, (int) (sum - labelHeight / 2));
			}
			g.setColor(0x00eeeeee);
			g.drawLine(leftPadding, (int) sum, width, (int) sum);
			g.setColor(0x00000000);
			g.drawLine(leftPadding, (int) sum, leftPadding + tickSize, (int) sum);
		}

		// Labels and ticks for X scale
		sum = leftPadding + horizontalStep * 31;	// padding for the first month
		String monthName;
		int monthNameLength;
		int lastSum = 0;
		for (int i = 1; i <= months; i++) {
			monthName = MyDate.getShortMonthName(i);
			monthNameLength = font.stringWidth(monthName);
			if (sum > lastSum) {
				g.drawString(monthName, (int) (sum - monthNameLength / 2), bottom + fontPadding);
				lastSum = (int)sum + monthNameLength + 5;
			}
			g.setColor(0x00eeeeee);
			g.drawLine((int) sum, 0, (int) sum, bottom);
			g.setColor(0x00000000);

			g.drawLine((int) sum, bottom, (int) sum, bottom - tickSize);
			sum += horizontalStep * MyDate.getDaysInMonth(i, date.getYear());
		}
		
		// Draw the BMI levels
		g.setColor(0x00aaaaaa);
//		g.setStrokeStyle(Graphics.DOTTED);
		g.setClip(leftPadding, 0, width, height - bottomPadding);
		
		drawBmiLine(g, width, Bmi.SEVERELY_UNDERWEIGHT, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.UNDERWEIGHT, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.NORMAL, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.OVERWEIGHT, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.OBESE_I, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.OBESE_II, prefs.getUserHeight());
		drawBmiLine(g, width, Bmi.OBESE_III, prefs.getUserHeight());
		
		g.setColor(0x00000000);
//		g.setStrokeStyle(Graphics.SOLID);
		g.setClip(0, 0, width, height);

		g.drawLine(leftPadding, 0, leftPadding, bottom);
		g.drawLine(leftPadding, bottom, width, bottom);
	}

	private void drawBmiLine(Graphics g, int width, double bmi, double height) {
		double bmiLine;
		int x, y;
		String levelName;
		g.setFont(font);
		bmiLine = Bmi.getWeight(bmi, height, prefs.isMetricUnits());
		y = (int) (verticalStep * (maxValue - bmiLine)) + topPadding;
		levelName = Bmi.getLevelName(bmi);
		levelName = L.l(levelName);
		x = width - font.stringWidth(levelName) - fontPadding;
		g.drawString(levelName, x, y - font.getHeight());
		g.drawLine(leftPadding, y, width, y);
		levelName = Bmi.getLevelName(bmi-1);
		levelName = L.l(levelName);
		x = width - font.stringWidth(levelName) - fontPadding;
		g.drawString(levelName, x, y + 1);
	}

	private void drawPlan(Graphics g, int month, int year, Plan plan) {
		MyDate chartDate = new MyDate(1, month, year);
		MyDate startDate = plan.getStartDate();
		MyDate endDate = plan.getEndDate();
		double startValue = plan.getStartWeight();
		double endValue = plan.getGoalWeight();

		int x, y, x1, y1;

		x = (int) (horizontalStep * (chartDate.differenceInDays(startDate)+1)) + leftPadding;
		y = (int) (verticalStep * (maxValue - startValue)) + topPadding;
		x1 = (int) (horizontalStep * (chartDate.differenceInDays(endDate)+1)) + leftPadding;
		y1 = (int) (verticalStep * (maxValue - endValue)) + topPadding;

		g.setColor(0x00ff9900);
//		g.setStrokeStyle(Graphics.DOTTED);
		g.drawLine(x, y, x1, y1);
		
		// Once the objective has been reached, the plan should stabilize
		if (endValue == plan.getGoalWeight()) {
			x = (int) (horizontalStep * (MyDate.getDaysInMonth(month, year))) + leftPadding;
			g.drawLine(x1, y1, x, y1);
		}
//		g.setStrokeStyle(Graphics.SOLID);
	}

	private void drawLine(Graphics g, Vector values, Value prevValue, Value nextValue) {
		int x = 0, yWeight, yTrend = 0, prevX = 0, prevY = 0;
		int valuesSize = values.size();
		if (valuesSize == 0) return;
		MyDate firstDay = new MyDate(1, 12, ((Value)values.firstElement()).date.getYear() - 1);		// The axis line represents December of the previous year
		Value value = null;
		for (int i = 0; i < valuesSize; i++) {
			value = (Value) values.elementAt(i);
			x = (int) (horizontalStep * (firstDay.differenceInDays(value.date))) + leftPadding;
			yWeight = (int) (verticalStep * (maxValue - value.weight)) + topPadding;
			yTrend = (int) (verticalStep * (maxValue - value.trend)) + topPadding;

			// Sinks and floaters
			g.setColor(0x0000ff00);
			g.drawLine(x, yWeight, x, yTrend);
			g.setColor(0x00555555);
			g.drawRect(x, yWeight, 0, 0);

			// Trend line
			g.setColor(0x00ff0000);
			if (i == 0) {
				if (prevValue == null) {
					prevX = x;
					prevY = yTrend;
				}
				else {
					prevX = (int) (horizontalStep * value.date.differenceInDays(prevValue.date)) + leftPadding;
					prevY = (int) (verticalStep * (maxValue - prevValue.trend)) + topPadding;
				}
			}
			g.drawLine(prevX, prevY, x, yTrend);
			prevX = x;
			prevY = yTrend;
		}
		if (nextValue != null) {
			x += (int) (horizontalStep * value.date.differenceInDays(nextValue.date)) + leftPadding;
			yTrend = (int) (verticalStep * (maxValue - nextValue.trend)) + topPadding;
			g.drawLine(prevX, prevY, x, yTrend);
		}
	}

	private void determineMinMax(Vector values) {
		int valuesSize = values.size();
		if (valuesSize == 0) {
			minValue = 80;
			maxValue = 85;
			return;
		}
		Value value;
		double min = 1000;
		double max = -1;
		for (int i = 0; i < valuesSize; i++) {
			value = (Value) values.elementAt(i);
			if (value.weight < min) {
				min = value.weight;
			}
			if (value.trend < min) {
				min = value.trend;
			}
			if (value.weight > max) {
				max = value.weight;
			}
			if (value.trend > max) {
				max = value.trend;
			}
		}
		minValue = (int) Math.floor(min - minMaxPadding);
		maxValue = max + minMaxPadding;
		if (maxValue - minValue < 1) {
			maxValue = minValue + 1;
		}
	}
}
